
#ifndef _PCIe_SATA_CONTROLLER_H
#define _PCIe_SATA_CONTROLLER_H

#include "PCIe_port.h"
#include "PCIe_Endpoint.h"
#include "SATA_command.h"

//#define DEBUG_SATA

const unsigned	MAX_BUFFER_SATA						= 8;
const unsigned	MAX_WR_BUFFER_SATA					= 16;
const unsigned	MAX_RD_BUFFER_SATA					= 16;
const unsigned	SATA_NUM_OF_VIRTUAL_CHANNEL_SATA	= 1;

const unsigned	DMA_BUFFER_SIZE						= 1024; //DMA buffer size: 4KB 1024*4

//H2D FSM
const unsigned HT_HostIdle	= 0;
const unsigned HT_CmdFIS	= 1;
const unsigned HT_CtrlFIS	= 2;
const unsigned HT_RegHDFIS1	= 3;
const unsigned HT_RegHDFIS2	= 4;
const unsigned HT_RegHDFIS3	= 5;
const unsigned HT_RegHDFIS4	= 6;

//D2H FSM
const unsigned HT_ChkTyp	= 7;
const unsigned HT_RegDHFIS0 = 8;
const unsigned HT_RegDHFIS1 = 9;
const unsigned HT_RegDHFIS2 = 10;
const unsigned HT_RegDHFIS3 = 11;
const unsigned HT_RegDHFIS4 = 12;
const unsigned HT_DMAACTFIS = 13;
const unsigned HT_DMAOTrans1 = 14;
const unsigned HT_DMAOTrans2 = 15;
const unsigned HT_DMAITrans = 16;

class PCIe_SATA_Controller
: public sc_module
, public PCIe_if
, public PCIe_Endpoint
{
public:
	sc_in_clk		PCIe_CLK;
	sc_in_clk		SATA_CLK;
    sc_in<bool>		RSTn;

	sc_out<int>		HTx_Dword; //SATA controller to SSD
	sc_in<int>		HRx_Dword; //SSD to SATA controller
			
	PCIe_id_t		id;
	PCIe_port		SATA_port;
	PCIe_lane_t		SATA_lane;

	virtual void
	PL_send_symbol(PCIe_id_t		id_,
				   PCIe_symbol_t	symbol_);

	virtual const bool
	DLL_send_sequence_number_byte(PCIe_id_t	id_,
								  byte		seq_num_);

	virtual const bool
	DLL_send_type_byte(PCIe_id_t	id_,
					   byte			type_byte_);

	virtual const bool
	DLL_send_data_byte(PCIe_id_t	id_,
					   byte			data_byte_);

	virtual const bool
	DLL_send_CRC_byte(PCIe_id_t	id_,
					  byte		CRC_byte_);

	virtual const bool
	DLL_send_LCRC_byte(PCIe_id_t	id_,
					   byte			LCRC_byte_);

	virtual void
	TL_send_header_byte(PCIe_id_t	id_,
						byte		header_byte_);

	virtual void
	TL_send_data_byte(PCIe_id_t	id_,
					  byte		data_byte_);

	virtual void
	TL_send_ECRC_byte(PCIe_id_t	id_,
					  byte		ECRC_byte_);

    SC_HAS_PROCESS(PCIe_SATA_Controller);

	PCIe_SATA_Controller(sc_module_name		name_,
						 PCIe_id_t			id_,
						 unsigned			South_Main_DMA_PRD_start_address_,
						 unsigned			South_Main_DMA_cmd_reg_start_address_,
						 unsigned			South_SSD_DMA_check_reg_start_addr_,
						 PCIe_address_space	addr_SATA_mem_1_,
						 PCIe_address_space	addr_SATA_io_,
						 PCIe_address_space	addr_SATA_conf_,
						 PCIe_address_space	addr_SATA_mem_2_,
						 unsigned			System_type_,
						 unsigned			DRAM_rank_index_,
						 unsigned			DRAM_bank_index_,
						 unsigned			DRAM_row_index_,
						 unsigned			DRAM_col_index_);
	
	~PCIe_SATA_Controller();

protected:
	unsigned			System_type;

	PCIe_address_map	SATA_address_map;

	unsigned			SATA_current_state;

	bool				SATA_is_STP;
	bool				SATA_is_NAK;

	bool				SATA_stop;

	unsigned short		SATA_sequence_number;
	unsigned short		SATA_NAK_sequence_number;
	unsigned short		SATA_NAK_free_sequence_number;
	byte				SATA_tag;

	PCIe_DLL_TLP		SATA_buffer_DLL_TLP_down[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_DLL_TLP_down_in;
	unsigned			SATA_buffer_DLL_TLP_down_out;
	unsigned			SATA_buffer_DLL_TLP_down_ack_nak;
	sc_time				SATA_buffer_DLL_TLP_down_time;

	PCIe_DLL_TLP		SATA_buffer_DLL_TLP_up[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_DLL_TLP_up_in;
	unsigned			SATA_buffer_DLL_TLP_up_out;
	unsigned			SATA_buffer_DLL_TLP_up_ack_nak;
	sc_time				SATA_buffer_DLL_TLP_up_time;

	PCIe_TLP			SATA_buffer_TLP_down[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_TLP_down_in;
	unsigned			SATA_buffer_TLP_down_out;
	sc_time				SATA_buffer_TLP_down_time;

	PCIe_TLP			SATA_buffer_TLP_up[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_TLP_up_in;
	unsigned			SATA_buffer_TLP_up_out;
	sc_time				SATA_buffer_TLP_up_time;

	unsigned			SATA_DLL_TLP_seq_num_cnt;
	unsigned			SATA_TLP_header_cnt;
	unsigned			SATA_TLP_data_cnt;
	unsigned			SATA_TLP_ECRC_cnt;
	unsigned			SATA_DLL_TLP_LCRC_cnt;

	PCIe_DLLP			SATA_buffer_DLLP_down[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_DLLP_down_in;
	unsigned			SATA_buffer_DLLP_down_out;
	sc_time				SATA_buffer_DLLP_down_time;

	PCIe_DLLP			SATA_buffer_DLLP_up[MAX_BUFFER_SATA];
	unsigned			SATA_buffer_DLLP_up_in;
	unsigned			SATA_buffer_DLLP_up_out;
	sc_time				SATA_buffer_DLLP_up_time;

	unsigned			SATA_DLLP_data_cnt;
	unsigned			SATA_DLLP_CRC_cnt;

	unsigned			SATA_granted_packet_type;

	unsigned			DRAM_rank_index;
	unsigned			DRAM_bank_index;
	unsigned			DRAM_row_index;
	unsigned			DRAM_col_index;

	unsigned			South_SSD_DMA_command;
	unsigned			South_SSD_DMA_sector_cnt;
	unsigned			South_SSD_DMA_LBA;
	unsigned			South_SSD_DMA_PRD_length;

	unsigned			South_Main_DMA_end;
	unsigned			South_SSD_DMA_end;
	bool				South_SSD_DMA_end_check;

	// DMA controller R/W BUFFER
	unsigned			WR_BUFFER[MAX_WR_BUFFER_SATA][DMA_BUFFER_SIZE]; // 4KB buffer
	unsigned			WR_BUFFER_in;
	unsigned			WR_BUFFER_out;
	unsigned			WR_pOUT;
	unsigned			WR_pIN;

	unsigned			RD_BUFFER[MAX_RD_BUFFER_SATA][DMA_BUFFER_SIZE]; // 4KB buffer
	unsigned			RD_BUFFER_in;
	unsigned			RD_BUFFER_out;
	unsigned			RD_pOUT;
	unsigned			RD_pIN;
	
	//Shadow block register contents
	byte				Hcommand;
	unsigned			LBA;
	unsigned			LBA_Extend;
	byte				Control;
	byte				Sector_cnt;
	byte				Shadow_status;
	byte				Shadow_DevHead;
	bool				ssd_interrupt;

	unsigned			PRD_length;

	//Reg H2D FIS reg.
	unsigned			Reg_HD0;
	unsigned			Reg_HD1;
	unsigned			Reg_HD2;
	unsigned			Reg_HD3;

	//DMA registers
	unsigned			DMA_base_addr[MAX_BUFFER_SATA];
	unsigned			DMA_base_cnt[MAX_BUFFER_SATA];
	unsigned			DMA_PRD_info_buffer_in;
	unsigned			DMA_PRD_info_buffer_out;

	//
	PCIe_address		South_Main_DMA_PRD_start_address_init;
	PCIe_address		South_Main_DMA_PRD_start_address;
	PCIe_address		South_Main_DMA_cmd_reg_start_address;
	PCIe_address		South_SSD_DMA_check_reg_start_addr;	

	//Temporary enable signal
	bool				Shadow_cmd_reg_wr;
	bool				Shadow_ctrl_wr;
	bool				PRD_req;
	bool				wr_transfer_ready;
	sc_time				wr_transfer_ready_time;
	bool				wr_buffer_flush;
	bool				next_rd_buffer_transfer;
	bool				rd_buffer_in_finished;
	bool				rd_last;
	
	char				FIS_type;
	bool				start_CmdFIS;
	unsigned			Htransport_currentstate;
	unsigned			Htransfer_cnt;
	unsigned			frame_receipt;

	byte				DMA_PRD_tag[MAX_BUFFER_SATA];
	unsigned			DMA_PRD_tag_buffer_in;
	unsigned			DMA_PRD_tag_buffer_out;
	byte				DMA_DATA_tag[MAX_BUFFER_SATA];
	unsigned			DMA_DATA_tag_buffer_in;
	unsigned			DMA_DATA_tag_buffer_out;

	unsigned			DMA_PRD_req_buffer_in;
	unsigned			DMA_PRD_req_buffer_out;

	const bool
	arbitrate_port(PCIe_id_t	id_,
				   unsigned		packet_type_);

	void
	SATA_make_action();

	void
	SATA_TLP_send_action();

	void
	SATA_ack_nak_action();

	void
	SATA_DLLP_send_action();

	void 
	Htransport_FSM_process();
	
	void 
	DMA_receive_completion_process();

	void
	DMA_send_request_process();

	void
	SSD_transfer_process();

	void
	PRD_fetch_process();
	
	void 
	Register_process();

	void
	Interrupt_check_process();
};

#endif